Filter
glimpse(flights)
Observations: 336,776
Variables: 19
$ year <int> 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, ...
$ month <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
$ day <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ...
$ dep_time <int> 517, 533, 542, 544, 554, 554, 555, 557, 557, 558, 558,...
$ sched_dep_time <int> 515, 529, 540, 545, 600, 558, 600, 600, 600, 600, 600,...
$ dep_delay <dbl> 2, 4, 2, -1, -6, -4, -5, -3, -3, -2, -2, -2, -2, -2, -...
$ arr_time <int> 830, 850, 923, 1004, 812, 740, 913, 709, 838, 753, 849...
$ sched_arr_time <int> 819, 830, 850, 1022, 837, 728, 854, 723, 846, 745, 851...
$ arr_delay <dbl> 11, 20, 33, -18, -25, 12, 19, -14, -8, 8, -2, -3, 7, -...
$ carrier <chr> "UA", "UA", "AA", "B6", "DL", "UA", "B6", "EV", "B6", ...
$ flight <int> 1545, 1714, 1141, 725, 461, 1696, 507, 5708, 79, 301, ...
$ tailnum <chr> "N14228", "N24211", "N619AA", "N804JB", "N668DN", "N39...
$ origin <chr> "EWR", "LGA", "JFK", "JFK", "LGA", "EWR", "EWR", "LGA"...
$ dest <chr> "IAH", "IAH", "MIA", "BQN", "ATL", "ORD", "FLL", "IAD"...
$ air_time <dbl> 227, 227, 160, 183, 116, 150, 158, 53, 140, 138, 149, ...
$ distance <dbl> 1400, 1416, 1089, 1576, 762, 719, 1065, 229, 944, 733,...
$ hour <dbl> 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, ...
$ minute <dbl> 15, 29, 40, 45, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, ...
$ time_hour <dttm> 2013-01-01 05:00:00, 2013-01-01 05:00:00, 2013-01-01 ...
Exercises
Find all flights that
- Had an arrival delay of two or more hours
- Flew to Houston (IAH or HOU)
- Were operated by United, American, or Delta
- Departed in summer (July, August, and September)
- Arrived more than two hours late, but didn’t leave late
- Were delayed by at least an hour, but made up over 30 minutes in flight
Departed between midnight and 6am (inclusive)
Had an arrival delay of two or more hours Since delay is in minutes, we are looking for flights where arr_delay > 120:
flights %>%
filter(arr_delay > 120)
Flew to Houston (IAH or HOU):
flights %>%
filter(dest %in% c("IAH", "HOU"))
Were operated by United, American, or Delta The variable carrier has the airline: but it is in two-digit carrier codes. However, we can look it up in the airlines dataset.
airlines
Since there are only 16 rows, its not even worth filtering. Delta is DL, American is AA, and United is UA:
filter(flights, carrier %in% c("AA", "DL", "UA"))
Departed in summer (July, August, and September) The variable month has the month, and it is numeric.
filter(flights, between(month, 7, 9))
Arrived more than two hours late, but didn’t leave late
filter(flights, !is.na(dep_delay), dep_delay <= 0, arr_delay > 120)
Were delayed by at least an hour, but made up over 30 minutes in flight
filter(flights, !is.na(dep_delay), dep_delay >= 60, arr_delay < 30)
Departed between midnight and 6am (inclusive).
filter(flights, dep_time >= 0, dep_time <= 600)
or using between (see next question)
filter(flights, between(dep_time, 0, 600))
- Another useful dplyr filtering helper is
between(). What does it do? Can you use it to simplify the code needed to answer the previous challenges?
between(x, left, right) is equivalent to x >= left & x <= right. I already used it in 1.4.
- How many flights have a missing
dep_time? What other variables are missing? What might these rows represent?
filter(flights, is.na(dep_time))
Since arr_time is also missing, these are cancelled flights.
- Why is
NA ^ 0 not missing? Why is NA | TRUE not missing? Why is FALSE & NA not missing? Can you figure out the general rule? (NA * 0 is a tricky counterexample!)
NA ^ 0 == 1 since for all numeric values \(x ^ 0 = 1\).
NA ^ 0
[1] 1
NA | TRUE is TRUE because the it doesn’t matter whether the missing value is TRUE or FALSE, x \lor T = T for all values of x.
NA | TRUE
Likewise, anything and FALSE is always FALSE.
NA & FALSE
[1] FALSE
Because the value of the missing element matters in NA | FALSE and NA & TRUE, these are missing:
NA | FALSE
[1] NA
NA & TRUE
[1] NA
wut? Since x * 0 = 0 for all \(x\) we might expect NA * 0 = 0, but that’s not the case.
NA * 0
[1] NA
Arrange
missing values always at the end.
Exercises
- How could you use
arrange() to sort all missing values to the start? (Hint: use is.na()).
This sorts by increasing dep_time, but with all missing values put first.
arrange(flights, desc(is.na(dep_time)), desc(dep_time))
- Sort flights to find the most delayed flights. Find the flights that left earliest.
The most delayed flights are found by sorting by dep_delay in descending order.
arrange(flights, desc(dep_delay))
If we sort dep_delay in ascending order, we get those that left earliest. There was a flight that left 43 minutes early.
arrange(flights, dep_delay)
- Sort flights to find the fastest flights.
I assume that by by “fastest flights” it means the flights with the minimum air time. So I sort by air_time. The fastest flights. The fastest flights area couple of flights between EWR and BDL with an air time of 20 minutes.
arrange(flights, air_time)
- Which flights travelled the longest? Which travelled the shortest?
I’ll assume hat travelled the longest or shortest refers to distance, rather than air-time.
The longest flights are the Hawaii Air (HA 51) between JFK and HNL (Honolulu) at 4,983 miles.
arrange(flights, desc(distance))
Apart from an EWR to LGA flight that was cancelled, the shortest flights are the Envoy Air Flights between EWR and PHL at 80 miles.
arrange(flights, distance)
- Brainstorm as many ways as possible to select
dep_time, dep_delay, arr_time, and arr_delay from flights.
A few ways include:
select(dep_time, dep_delay, arr_time, arr_delay)
select(starts_with("dep_"), starts_with("arr_"))
select(matches("^(dep|arr)_(time|delay)$"))
using ends_with() doesn’t work well since it would bget sched_arr_time and sched_dep_time.
- What happens if you include the name of a variable multiple times in a select() call?
It ignores the duplicates, and that variable is only included once. No error, warning, or message is emited.
select(flights, year, month, day, year, year)
- What does the
one_of() function do? Why might it be helpful in conjunction with this vector?
The one_of vector allows you to select variables with a character vector rather than as unquoted variable names. It’s useful because then you can easily pass vectors to select().
vars <- c("year", "month", "day", "dep_delay", "arr_delay")
select(flights, one_of(vars))
- Does the result of running the following code surprise you? How do the select helpers deal with case by default? How can you change that default?
select(flights, contains("TIME"))
The default behavior for contains is to ignore case. Yes, it surprises me. Upon reflection, I realized that this is likely the default behavior because dplyr is designed to deal with a variety of data backends, and some database engines don’t differentiate case.
To change the behavior add the argument ignore.case = FALSE. Now no variables are selected.
select(flights, contains("TIME", ignore.case = FALSE))
Mutate
Exercises
- Currently
dep_time and sched_dep_time are convenient to look at, but hard to compute with because they’re not really continuous numbers. Convert them to a more convenient representation of number of minutes since midnight.
To get the departure times in the number of minutes, (integer) divide dep_time by 100 to get the hours since midnight and muliply by 60 and add the remainder of dep_time divided by 100.
mutate(flights,
dep_time_mins = dep_time %/% 100 * 60 + dep_time %% 100,
sched_dep_time_mins = sched_dep_time %/% 100 * 60 + sched_dep_time %% 100) %>%
select(dep_time, dep_time_mins, sched_dep_time, sched_dep_time_mins)
This would be more cleanly done by first definining a funciton and reusing that:
time2mins <- function(x) {
x %/% 100 * 60 + x %% 100
}
mutate(flights,
dep_time_mins = time2mins(dep_time),
sched_dep_time_mins = time2mins(sched_dep_time)) %>%
select(dep_time, dep_time_mins, sched_dep_time, sched_dep_time_mins)
- Compare
air_time with arr_time - dep_time. What do you expect to see? What do you see? What do you need to do to fix it?
Since arr_time and dep_time may be in different time zones, the air_time doesn’t equal the difference. We would need to account for time-zones in these calculations.
mutate(flights,
air_time2 = arr_time - dep_time,
air_time_diff = air_time2 - air_time) %>%
filter(air_time_diff != 0) %>%
select(air_time, air_time2, dep_time, arr_time, dest)
- Compare
dep_time, sched_dep_time, and dep_delay. How would you expect those three numbers to be related?
I’d expect dep_time, sched_dep_time, and dep_delay to be related so that dep_time - sched_dep_time = dep_delay.
mutate(flights,
dep_delay2 = dep_time - sched_dep_time) %>%
filter(dep_delay2 != dep_delay) %>%
select(dep_time, sched_dep_time, dep_delay, dep_delay2)
Oops, I forgot to convert to minutes. I’ll reuse the time2mins function I wrote earlier.
mutate(flights,
dep_delay2 = time2mins(dep_time) - time2mins(sched_dep_time)) %>%
filter(dep_delay2 != dep_delay) %>%
select(dep_time, sched_dep_time, dep_delay, dep_delay2)
Well, that solved most of the problems, but these two numbers don’t match because we aren’t accounting for flights where the departure time is the next day from the scheduled departure time.
- Find the 10 most delayed flights using a ranking function. How do you want to handle ties? Carefully read the documentation for
min_rank().
I’d want to handle ties by taking the minimum of tied values. If three flights are have the same value and are the most delayed, we would say they are tied for first, not tied for third or second.
mutate(flights,
dep_delay_rank = min_rank(-dep_delay)) %>%
arrange(dep_delay_rank) %>%
filter(dep_delay_rank <= 10)
- What does
1:3 + 1:10 return? Why?
It returns c(1 + 1, 2 + 2, 3 + 3, 1 + 4, 2 + 5, 3 + 6, 1 + 7, 2 + 8, 3 + 9, 1 + 10). When adding two vectors recycles the shorter vector’s values to get vectors of the same length. We get a warning vector since the shorter vector is not a multiple of the longer one (this often, but not necessarily, means we made an error somewhere).
1:3 + 1:10
longer object length is not a multiple of shorter object length
[1] 2 4 6 5 7 9 8 10 12 11
- What trigonometric functions does R provide?
All the classics: cos, sin, tan, acos, asin, atan, plus a few others that are drive by numerical or computational issues.
Exercises
- Brainstorm at least 5 different ways to assess the typical delay characteristics of a group of flights. Consider the following scenarios:
A flight is 15 minutes early 50% of the time, and 15 minutes late 50% of the time.
A flight is always 10 minutes late.
A flight is 30 minutes early 50% of the time, and 30 minutes late 50% of the time.
99% of the time a flight is on time. 1% of the time it’s 2 hours late.
Which is more important: arrival delay or departure delay?
Arrival delay is more important. Arriving early is nice, but equally as good as arriving late is bad. Variation is worse than consistency; if I know the plane will always arrive 10 minutes late, then I can plan for it arriving as if the actual arrival time was 10 minutes later than the scheduled arrival time.
So I’d try something that calculates the expected time of the flight, and then aggregates over any delays from that time. I would ignore any early arrival times. A better ranking would also consider cancellations, and need a way to convert them to a delay time (perhaps using the arrival time of the next flight to the same destination).
Come up with another approach that will give you the same output as not_cancelled %>% count(dest) and not_cancelled %>% count(tailnum, wt = distance) (without using count()).
Our definition of cancelled flights (is.na(dep_delay) | is.na(arr_delay)) is slightly suboptimal. Why? Which is the most important column?
If a flight doesn’t depart, then it won’t arrive. A flight can also depart and not arrive if it crashes; I’m not sure how this data would handle flights that are redirected and land at other airports for whatever reason.
The more important column is arr_delay so we could just use that.
filter(flights, !is.na(dep_delay), is.na(arr_delay)) %>%
select(dep_time, arr_time, sched_arr_time, dep_delay, arr_delay)
Okay, I’m not sure what’s going on in this data. dep_time can be non-missing and arr_delay missing but arr_time not missing. They may be combining different flights?
- Look at the number of cancelled flights per day. Is there a pattern? Is the proportion of cancelled flights related to the average delay?
cancelled_delayed <-
flights %>%
mutate(cancelled = (is.na(arr_delay) | is.na(dep_delay))) %>%
group_by(year, month, day) %>%
summarise(prop_cancelled = mean(cancelled),
avg_dep_delay = mean(dep_delay, na.rm = TRUE))
ggplot(cancelled_delayed, aes(x = avg_dep_delay, prop_cancelled)) +
geom_point() +
geom_smooth()

- Which carrier has the worst delays? Challenge: can you disentangle the effects of bad airports vs. bad carriers? Why/why not? (Hint: think about
flights %>% group_by(carrier, dest) %>% summarise(n()))
flights %>%
group_by(carrier) %>%
summarise(arr_delay = mean(arr_delay, na.rm = TRUE)) %>%
arrange(desc(arr_delay))
filter(airlines, carrier == "F9")
Frontier Airlines (FL) has the worst delays.
You can get part of the way to disentangling the effects of airports vs. carriers by comparing each flight’s delay to the average delay of destination airport. However, you’d really want to compare it to the average delay of the desination airport, after removing other flights from the same airline.
538 has done something like this: http://fivethirtyeight.com/features/the-best-and-worst-airlines-airports-and-flights-summer-2015-update/.
- For each plane, count the number of flights before the first delay of greater than 1 hour.
I think this requires grouped mutate (but I may be wrong):
flights %>%
arrange(tailnum, year, month, day) %>%
group_by(tailnum) %>%
mutate(delay_gt1hr = dep_delay > 60) %>%
mutate(before_delay = cumsum(delay_gt1hr)) %>%
filter(before_delay < 60) %>%
count(sort = TRUE)
- What does the sort argument to
count() do. When might you use it?
The sort argument to count sorts the results in order of n. You could use this anytime you would do count followed by arrange.
Grouped mutates and filters
Exercises
- Refer back to the table of useful mutate and filtering functions. Describe how each operation changes when you combine it with grouping.
They operate within each group rather than over the entire data frame. E.g. mean will calculate the mean within each group.
- Which plane (tailnum) has the worst on-time record?
flights %>%
group_by(tailnum) %>%
summarise(arr_delay = mean(arr_delay, na.rm = TRUE)) %>%
ungroup() %>%
filter(rank(desc(arr_delay)) <= 1)
- What time of day should you fly if you want to avoid delays as much as possible?
Let’s group by hour. The earlier the better to fly. This is intuitive as delays early in the morning are likely to propogate throughout the day.
flights %>%
group_by(hour) %>%
summarise(arr_delay = mean(arr_delay, na.rm = TRUE)) %>%
ungroup() %>%
arrange(arr_delay)
- For each destination, compute the total minutes of delay. For each, flight, compute the proportion of the total delay for its destination.
flights %>%
filter(!is.na(arr_delay)) %>%
group_by(dest) %>%
mutate(total_delay = sum(arr_delay))
NA
Alternatively, consider the delay as relative to the minimum delay for any flight to that destination. Now all non-cancelled flights have a proportion.
flights %>%
filter(!is.na(arr_delay), arr_delay > 0) %>%
group_by(dest) %>%
mutate(total_delay = sum(arr_delay - min(arr_delay)),
prop_delay = arr_delay / sum(arr_delay))
- Delays are typically temporally correlated: even once the problem that caused the initial delay has been resolved, later flights are delayed to allow earlier flights to leave. Using
lag() explore how the delay of a flight is related to the delay of the immediately preceding flight.
We want to group by day to avoid taking the lag from the previous day. Also, I want to use departure delay, since this mechanism is relevant for departures. Also, I remove missing values both before and after calculating the lag delay. However, it would be interesting to ask the probability or averge delay after a cancellation.
flights %>%
group_by(year, month, day) %>%
filter(!is.na(dep_delay)) %>%
mutate(lag_delay = lag(dep_delay)) %>%
filter(!is.na(lag_delay)) %>%
ggplot(aes(x = dep_delay, y = lag_delay)) +
geom_point() +
geom_smooth()

- Look at each destination. Can you find flights that are suspiciously fast? (i.e. flights that represent a potential data entry error). Compute the air time a flight relative to the shortest flight to that destination. Which flights were most delayed in the air?
The shorter BOS and PHL flights that are 20 minutes for 30+ minutes flights seem plausible - though maybe entries of +/- a few minutes can easily create large changes. I assume that departure time has a standardized definition, but I’m not sure; if there is some discretion, that could create errors that are small in absolute time, but large in relative time for small flights. The ATL, GSP, an BNA flights looks a little suspicious as it’s almost half the time for longer flights.
flights %>%
filter(!is.na(air_time)) %>%
group_by(dest) %>%
mutate(med_time = median(air_time),
fast = (air_time - med_time) / med_time) %>%
arrange(fast) %>%
select(air_time, med_time, fast, dep_time, sched_dep_time, arr_time, sched_arr_time) %>%
head(15)
Adding missing grouping variables: `dest`
I could also try a z-score. Though the sd and mean will be affected by large delays.
flights %>%
filter(!is.na(air_time)) %>%
group_by(dest) %>%
mutate(air_time_mean = mean(air_time),
air_time_sd = sd(air_time),
z_score = (air_time - air_time_mean) / air_time_sd) %>%
arrange(z_score) %>%
select(z_score, air_time_mean, air_time_sd, air_time, dep_time, sched_dep_time, arr_time, sched_arr_time)
Adding missing grouping variables: `dest`
flights %>%
filter(!is.na(air_time)) %>%
group_by(dest) %>%
mutate(air_time_diff = air_time - min(air_time)) %>%
arrange(desc(air_time_diff)) %>%
select(dest, year, month, day, carrier, flight, air_time_diff, air_time, dep_time, arr_time) %>%
head()
- Find all destinations that are flown by at least two carriers. Use that information to rank the carriers.
The carrier tha flies to the most locations is ExpressJet Airlines (EV). ExpressJet is a regional airline and partner for major airlines, so its one of those that flies small planes to close airports
flights %>%
group_by(dest, carrier) %>%
count(carrier) %>%
group_by(carrier) %>%
count(sort = TRUE)
filter(airlines, carrier == "EV")
LS0tCnRpdGxlOiAiQ2ggNTogRGF0YSBUcmFuc2Zvcm1hdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMgSW50cm9kdWN0aW9uCgojIyBQcmVyZXF1aXNpdGVzCgpgYGB7cn0KbGlicmFyeShueWNmbGlnaHRzMTMpCmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCgojIEZpbHRlcgoKYGBge3J9CmdsaW1wc2UoZmxpZ2h0cykKYGBgCgojIyBFeGVyY2lzZXMKCjEuIEZpbmQgYWxsIGZsaWdodHMgdGhhdAoKICAxLiBIYWQgYW4gYXJyaXZhbCBkZWxheSBvZiB0d28gb3IgbW9yZSBob3VycwogIDIuIEZsZXcgdG8gSG91c3RvbiAoSUFIIG9yIEhPVSkKICAzLiBXZXJlIG9wZXJhdGVkIGJ5IFVuaXRlZCwgQW1lcmljYW4sIG9yIERlbHRhCiAgNC4gRGVwYXJ0ZWQgaW4gc3VtbWVyIChKdWx5LCBBdWd1c3QsIGFuZCBTZXB0ZW1iZXIpCiAgNS4gQXJyaXZlZCBtb3JlIHRoYW4gdHdvIGhvdXJzIGxhdGUsIGJ1dCBkaWRu4oCZdCBsZWF2ZSBsYXRlCiAgNi4gV2VyZSBkZWxheWVkIGJ5IGF0IGxlYXN0IGFuIGhvdXIsIGJ1dCBtYWRlIHVwIG92ZXIgMzAgbWludXRlcyBpbiBmbGlnaHQKICA3LiBEZXBhcnRlZCBiZXR3ZWVuIG1pZG5pZ2h0IGFuZCA2YW0gKGluY2x1c2l2ZSkKCipIYWQgYW4gYXJyaXZhbCBkZWxheSBvZiB0d28gb3IgbW9yZSBob3VycyogU2luY2UgZGVsYXkgaXMgaW4gbWludXRlcywgd2UgYXJlIGxvb2tpbmcKZm9yIGZsaWdodHMgd2hlcmUgYGFycl9kZWxheSA+IDEyMGA6CmBgYHtyfQpmbGlnaHRzICU+JSAKICBmaWx0ZXIoYXJyX2RlbGF5ID4gMTIwKQpgYGAKCipGbGV3IHRvIEhvdXN0b24gKElBSCBvciBIT1UpKjoKYGBge3J9CmZsaWdodHMgJT4lCiAgZmlsdGVyKGRlc3QgJWluJSBjKCJJQUgiLCAiSE9VIikpCmBgYAoKKldlcmUgb3BlcmF0ZWQgYnkgVW5pdGVkLCBBbWVyaWNhbiwgb3IgRGVsdGEqIFRoZSB2YXJpYWJsZSBgY2FycmllcmAgaGFzIHRoZSBhaXJsaW5lOiBidXQgaXQgaXMgaW4gdHdvLWRpZ2l0IGNhcnJpZXIgY29kZXMuIEhvd2V2ZXIsIHdlIGNhbiBsb29rIGl0IHVwIGluIHRoZSBgYWlybGluZXNgCmRhdGFzZXQuCmBgYHtyfQphaXJsaW5lcwpgYGAKU2luY2UgdGhlcmUgYXJlIG9ubHkgMTYgcm93cywgaXRzIG5vdCBldmVuIHdvcnRoIGZpbHRlcmluZy4KRGVsdGEgaXMgYERMYCwgQW1lcmljYW4gaXMgYEFBYCwgYW5kIFVuaXRlZCBpcyBgVUFgOgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIGNhcnJpZXIgJWluJSBjKCJBQSIsICJETCIsICJVQSIpKQpgYGAKCipEZXBhcnRlZCBpbiBzdW1tZXIgKEp1bHksIEF1Z3VzdCwgYW5kIFNlcHRlbWJlcikqIFRoZSB2YXJpYWJsZSBgbW9udGhgIGhhcyB0aGUgbW9udGgsIGFuZCBpdCBpcyBudW1lcmljLgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIGJldHdlZW4obW9udGgsIDcsIDkpKQpgYGAKCipBcnJpdmVkIG1vcmUgdGhhbiB0d28gaG91cnMgbGF0ZSwgYnV0IGRpZG7igJl0IGxlYXZlIGxhdGUqCmBgYHtyfQpmaWx0ZXIoZmxpZ2h0cywgIWlzLm5hKGRlcF9kZWxheSksIGRlcF9kZWxheSA8PSAwLCBhcnJfZGVsYXkgPiAxMjApCmBgYAoKKldlcmUgZGVsYXllZCBieSBhdCBsZWFzdCBhbiBob3VyLCBidXQgbWFkZSB1cCBvdmVyIDMwIG1pbnV0ZXMgaW4gZmxpZ2h0KgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsICFpcy5uYShkZXBfZGVsYXkpLCBkZXBfZGVsYXkgPj0gNjAsIGFycl9kZWxheSA8IDMwKQpgYGAKCipEZXBhcnRlZCBiZXR3ZWVuIG1pZG5pZ2h0IGFuZCA2YW0gKGluY2x1c2l2ZSkqLgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIGRlcF90aW1lID49IDAsIGRlcF90aW1lIDw9IDYwMCkKYGBgCm9yIHVzaW5nIGBiZXR3ZWVuYCAoc2VlIG5leHQgcXVlc3Rpb24pCmBgYHtyfQpmaWx0ZXIoZmxpZ2h0cywgYmV0d2VlbihkZXBfdGltZSwgMCwgNjAwKSkKYGBgCgoKMi4gQW5vdGhlciB1c2VmdWwgZHBseXIgZmlsdGVyaW5nIGhlbHBlciBpcyBgYmV0d2VlbigpYC4gV2hhdCBkb2VzIGl0IGRvPyBDYW4geW91IHVzZSBpdCB0byBzaW1wbGlmeSB0aGUgY29kZSBuZWVkZWQgdG8gYW5zd2VyIHRoZSBwcmV2aW91cyBjaGFsbGVuZ2VzPwoKYGJldHdlZW4oeCwgbGVmdCwgcmlnaHQpYCBpcyBlcXVpdmFsZW50IHRvIGB4ID49IGxlZnQgJiB4IDw9IHJpZ2h0YC4gSSBhbHJlYWR5IAp1c2VkIGl0IGluIDEuNC4KCjMuIEhvdyBtYW55IGZsaWdodHMgaGF2ZSBhIG1pc3NpbmcgYGRlcF90aW1lYD8gV2hhdCBvdGhlciB2YXJpYWJsZXMgYXJlIG1pc3Npbmc/IFdoYXQgbWlnaHQgdGhlc2Ugcm93cyByZXByZXNlbnQ/CgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsIGlzLm5hKGRlcF90aW1lKSkKYGBgCgpTaW5jZSBgYXJyX3RpbWVgIGlzIGFsc28gbWlzc2luZywgdGhlc2UgYXJlIGNhbmNlbGxlZCBmbGlnaHRzLgoKNC4gV2h5IGlzIGBOQSBeIDBgIG5vdCBtaXNzaW5nPyBXaHkgaXMgYE5BIHwgVFJVRWAgbm90IG1pc3Npbmc/IFdoeSBpcyBgRkFMU0UgJiBOQWAgbm90IG1pc3Npbmc/IENhbiB5b3UgZmlndXJlIG91dCB0aGUgZ2VuZXJhbCBydWxlPyAoYE5BICogMGAgaXMgYSB0cmlja3kgY291bnRlcmV4YW1wbGUhKQoKYE5BIF4gMCA9PSAxYCBzaW5jZSBmb3IgYWxsIG51bWVyaWMgdmFsdWVzICR4IF4gMCA9IDEkLiAKYGBge3J9Ck5BIF4gMApgYGAKCmBOQSB8IFRSVUVgIGlzIGBUUlVFYCBiZWNhdXNlIHRoZSBpdCBkb2Vzbid0IG1hdHRlciB3aGV0aGVyIHRoZSBtaXNzaW5nIHZhbHVlIGlzIGBUUlVFYCBvciBgRkFMU0VgLCBgeCBcbG9yIFQgPSBUYCBmb3IgYWxsIHZhbHVlcyBvZiBgeGAuCmBgYHtyfQpOQSB8IFRSVUUKYGBgCkxpa2V3aXNlLCBhbnl0aGluZyBhbmQgYEZBTFNFYCBpcyBhbHdheXMgYEZBTFNFYC4KYGBge3J9Ck5BICYgRkFMU0UKYGBgCkJlY2F1c2UgdGhlIHZhbHVlIG9mIHRoZSBtaXNzaW5nIGVsZW1lbnQgbWF0dGVycyBpbiBgTkEgfCBGQUxTRWAgYW5kIGBOQSAmIFRSVUVgLCB0aGVzZSBhcmUgbWlzc2luZzoKYGBge3J9Ck5BIHwgRkFMU0UKTkEgJiBUUlVFCmBgYAoKd3V0PyBTaW5jZSBgeCAqIDAgPSAwYCBmb3IgYWxsICR4JCB3ZSBtaWdodCBleHBlY3QgYE5BICogMCA9IDBgLCBidXQgdGhhdCdzIG5vdCB0aGUgY2FzZS4KYGBge3J9Ck5BICogMApgYGAKCiMgQXJyYW5nZQoKbWlzc2luZyB2YWx1ZXMgYWx3YXlzIGF0IHRoZSBlbmQuCgojIyBFeGVyY2lzZXMKCjEuIEhvdyBjb3VsZCB5b3UgdXNlIGBhcnJhbmdlKClgIHRvIHNvcnQgYWxsIG1pc3NpbmcgdmFsdWVzIHRvIHRoZSBzdGFydD8gKEhpbnQ6IHVzZSBgaXMubmEoKWApLgoKVGhpcyBzb3J0cyBieSBpbmNyZWFzaW5nIGBkZXBfdGltZWAsIGJ1dCB3aXRoIGFsbCBtaXNzaW5nIHZhbHVlcyBwdXQgZmlyc3QuCmBgYHtyfQphcnJhbmdlKGZsaWdodHMsIGRlc2MoaXMubmEoZGVwX3RpbWUpKSwgZGVwX3RpbWUpCmBgYAoKMi4gU29ydCBmbGlnaHRzIHRvIGZpbmQgdGhlIG1vc3QgZGVsYXllZCBmbGlnaHRzLiBGaW5kIHRoZSBmbGlnaHRzIHRoYXQgbGVmdCBlYXJsaWVzdC4KClRoZSBtb3N0IGRlbGF5ZWQgZmxpZ2h0cyBhcmUgZm91bmQgYnkgc29ydGluZyBieSBgZGVwX2RlbGF5YCBpbiBkZXNjZW5kaW5nIG9yZGVyLgpgYGB7cn0KYXJyYW5nZShmbGlnaHRzLCBkZXNjKGRlcF9kZWxheSkpCmBgYApJZiB3ZSBzb3J0IGBkZXBfZGVsYXlgIGluIGFzY2VuZGluZyBvcmRlciwgd2UgZ2V0IHRob3NlIHRoYXQgbGVmdCBlYXJsaWVzdC4KVGhlcmUgd2FzIGEgZmxpZ2h0IHRoYXQgbGVmdCA0MyBtaW51dGVzIGVhcmx5LgpgYGB7cn0KYXJyYW5nZShmbGlnaHRzLCBkZXBfZGVsYXkpCmBgYAoKMy4gU29ydCBmbGlnaHRzIHRvIGZpbmQgdGhlIGZhc3Rlc3QgZmxpZ2h0cy4KCkkgYXNzdW1lIHRoYXQgYnkgYnkgImZhc3Rlc3QgZmxpZ2h0cyIgaXQgbWVhbnMgdGhlIGZsaWdodHMgd2l0aCB0aGUgbWluaW11bSBhaXIgdGltZS4KU28gSSBzb3J0IGJ5IGBhaXJfdGltZWAuIFRoZSBmYXN0ZXN0IGZsaWdodHMuIFRoZSBmYXN0ZXN0IGZsaWdodHMgYXJlYSBjb3VwbGUgb2YgZmxpZ2h0cyBiZXR3ZWVuIEVXUiBhbmQgQkRMIHdpdGggYW4gYWlyIHRpbWUgb2YgMjAgbWludXRlcy4KYGBge3J9CmFycmFuZ2UoZmxpZ2h0cywgYWlyX3RpbWUpCmBgYAoKCjQuIFdoaWNoIGZsaWdodHMgdHJhdmVsbGVkIHRoZSBsb25nZXN0PyBXaGljaCB0cmF2ZWxsZWQgdGhlIHNob3J0ZXN0PwoKSSdsbCBhc3N1bWUgaGF0IHRyYXZlbGxlZCB0aGUgbG9uZ2VzdCBvciBzaG9ydGVzdCByZWZlcnMgdG8gZGlzdGFuY2UsIHJhdGhlciB0aGFuIGFpci10aW1lLgoKVGhlIGxvbmdlc3QgZmxpZ2h0cyBhcmUgdGhlIEhhd2FpaSBBaXIgKEhBIDUxKSBiZXR3ZWVuIEpGSyBhbmQgSE5MIChIb25vbHVsdSkgYXQgNCw5ODMgbWlsZXMuCmBgYHtyfQphcnJhbmdlKGZsaWdodHMsIGRlc2MoZGlzdGFuY2UpKQpgYGAKCkFwYXJ0IGZyb20gYW4gRVdSIHRvIExHQSBmbGlnaHQgdGhhdCB3YXMgY2FuY2VsbGVkLCB0aGUgc2hvcnRlc3QgZmxpZ2h0cyBhcmUgdGhlIEVudm95IEFpciBGbGlnaHRzIGJldHdlZW4gRVdSIGFuZCBQSEwgYXQgODAgbWlsZXMuCmBgYHtyfQphcnJhbmdlKGZsaWdodHMsIGRpc3RhbmNlKQpgYGAKCjEuIEJyYWluc3Rvcm0gYXMgbWFueSB3YXlzIGFzIHBvc3NpYmxlIHRvIHNlbGVjdCBgZGVwX3RpbWVgLCBgZGVwX2RlbGF5YCwgYGFycl90aW1lYCwgYW5kIGBhcnJfZGVsYXlgIGZyb20gZmxpZ2h0cy4KCkEgZmV3IHdheXMgaW5jbHVkZToKYGBge3J9CnNlbGVjdChkZXBfdGltZSwgZGVwX2RlbGF5LCBhcnJfdGltZSwgYXJyX2RlbGF5KQpzZWxlY3Qoc3RhcnRzX3dpdGgoImRlcF8iKSwgc3RhcnRzX3dpdGgoImFycl8iKSkKc2VsZWN0KG1hdGNoZXMoIl4oZGVwfGFycilfKHRpbWV8ZGVsYXkpJCIpKQpgYGAKdXNpbmcgYGVuZHNfd2l0aCgpYCBkb2Vzbid0IHdvcmsgd2VsbCBzaW5jZSBpdCB3b3VsZCBiZ2V0IGBzY2hlZF9hcnJfdGltZWAgYW5kIGBzY2hlZF9kZXBfdGltZWAuCgoyLiBXaGF0IGhhcHBlbnMgaWYgeW91IGluY2x1ZGUgdGhlIG5hbWUgb2YgYSB2YXJpYWJsZSBtdWx0aXBsZSB0aW1lcyBpbiBhIHNlbGVjdCgpIGNhbGw/CgpJdCBpZ25vcmVzIHRoZSBkdXBsaWNhdGVzLCBhbmQgdGhhdCB2YXJpYWJsZSBpcyBvbmx5IGluY2x1ZGVkIG9uY2UuIE5vIGVycm9yLCB3YXJuaW5nLCBvciBtZXNzYWdlIGlzIGVtaXRlZC4KYGBge3J9CnNlbGVjdChmbGlnaHRzLCB5ZWFyLCBtb250aCwgZGF5LCB5ZWFyLCB5ZWFyKQpgYGAKCjMuIFdoYXQgZG9lcyB0aGUgYG9uZV9vZigpYCBmdW5jdGlvbiBkbz8gV2h5IG1pZ2h0IGl0IGJlIGhlbHBmdWwgaW4gY29uanVuY3Rpb24gd2l0aCB0aGlzIHZlY3Rvcj8KClRoZSBgb25lX29mYCB2ZWN0b3IgYWxsb3dzIHlvdSB0byBzZWxlY3QgdmFyaWFibGVzIHdpdGggYSBjaGFyYWN0ZXIgdmVjdG9yIHJhdGhlciB0aGFuIGFzIHVucXVvdGVkIHZhcmlhYmxlIG5hbWVzLgpJdCdzIHVzZWZ1bCBiZWNhdXNlIHRoZW4geW91IGNhbiBlYXNpbHkgcGFzcyB2ZWN0b3JzIHRvIGBzZWxlY3QoKWAuCgpgYGB7cn0KdmFycyA8LSBjKCJ5ZWFyIiwgIm1vbnRoIiwgImRheSIsICJkZXBfZGVsYXkiLCAiYXJyX2RlbGF5IikKc2VsZWN0KGZsaWdodHMsIG9uZV9vZih2YXJzKSkKYGBgCgoKNC4gRG9lcyB0aGUgcmVzdWx0IG9mIHJ1bm5pbmcgdGhlIGZvbGxvd2luZyBjb2RlIHN1cnByaXNlIHlvdT8gSG93IGRvIHRoZSBzZWxlY3QgaGVscGVycyBkZWFsIHdpdGggY2FzZSBieSBkZWZhdWx0PyBIb3cgY2FuIHlvdSBjaGFuZ2UgdGhhdCBkZWZhdWx0PwoKYGBge3J9CnNlbGVjdChmbGlnaHRzLCBjb250YWlucygiVElNRSIpKQpgYGAKClRoZSBkZWZhdWx0IGJlaGF2aW9yIGZvciBjb250YWlucyBpcyB0byBpZ25vcmUgY2FzZS4KWWVzLCBpdCBzdXJwcmlzZXMgbWUuClVwb24gcmVmbGVjdGlvbiwgSSByZWFsaXplZCB0aGF0IHRoaXMgaXMgbGlrZWx5IHRoZSBkZWZhdWx0IGJlaGF2aW9yIGJlY2F1c2UgYGRwbHlyYCBpcyBkZXNpZ25lZCB0byBkZWFsIHdpdGggYSB2YXJpZXR5IG9mIGRhdGEgYmFja2VuZHMsIGFuZCBzb21lIGRhdGFiYXNlIGVuZ2luZXMgZG9uJ3QgZGlmZmVyZW50aWF0ZSBjYXNlLgoKVG8gY2hhbmdlIHRoZSBiZWhhdmlvciBhZGQgdGhlIGFyZ3VtZW50IGBpZ25vcmUuY2FzZSA9IEZBTFNFYC4gTm93IG5vIHZhcmlhYmxlcyBhcmUgc2VsZWN0ZWQuCmBgYHtyfQpzZWxlY3QoZmxpZ2h0cywgY29udGFpbnMoIlRJTUUiLCBpZ25vcmUuY2FzZSA9IEZBTFNFKSkKYGBgCgojIE11dGF0ZQoKIyMgRXhlcmNpc2VzCgoxLiBDdXJyZW50bHkgYGRlcF90aW1lYCBhbmQgYHNjaGVkX2RlcF90aW1lYCBhcmUgY29udmVuaWVudCB0byBsb29rIGF0LCBidXQgaGFyZCB0byBjb21wdXRlIHdpdGggYmVjYXVzZSB0aGV54oCZcmUgbm90IHJlYWxseSBjb250aW51b3VzIG51bWJlcnMuIENvbnZlcnQgdGhlbSB0byBhIG1vcmUgY29udmVuaWVudCByZXByZXNlbnRhdGlvbiBvZiBudW1iZXIgb2YgbWludXRlcyBzaW5jZSBtaWRuaWdodC4KClRvIGdldCB0aGUgZGVwYXJ0dXJlIHRpbWVzIGluIHRoZSBudW1iZXIgb2YgbWludXRlcywgKGludGVnZXIpIGRpdmlkZSBgZGVwX3RpbWVgIGJ5IDEwMCB0byBnZXQgdGhlIGhvdXJzIHNpbmNlIG1pZG5pZ2h0IGFuZCBtdWxpcGx5IGJ5IDYwIGFuZCBhZGQgdGhlIHJlbWFpbmRlciBvZiBgZGVwX3RpbWVgIGRpdmlkZWQgYnkgMTAwLgpgYGB7cn0KbXV0YXRlKGZsaWdodHMsCiAgICAgICBkZXBfdGltZV9taW5zID0gZGVwX3RpbWUgJS8lIDEwMCAqIDYwICsgZGVwX3RpbWUgJSUgMTAwLAogICAgICAgc2NoZWRfZGVwX3RpbWVfbWlucyA9IHNjaGVkX2RlcF90aW1lICUvJSAxMDAgKiA2MCArIHNjaGVkX2RlcF90aW1lICUlIDEwMCkgJT4lCiAgc2VsZWN0KGRlcF90aW1lLCBkZXBfdGltZV9taW5zLCBzY2hlZF9kZXBfdGltZSwgc2NoZWRfZGVwX3RpbWVfbWlucykKYGBgCgpUaGlzIHdvdWxkIGJlIG1vcmUgY2xlYW5seSBkb25lIGJ5IGZpcnN0IGRlZmluaW5pbmcgYSBmdW5jaXRvbiBhbmQgcmV1c2luZyB0aGF0OgpgYGB7cn0KdGltZTJtaW5zIDwtIGZ1bmN0aW9uKHgpIHsKICB4ICUvJSAxMDAgKiA2MCArIHggJSUgMTAwCn0KbXV0YXRlKGZsaWdodHMsCiAgICAgICBkZXBfdGltZV9taW5zID0gdGltZTJtaW5zKGRlcF90aW1lKSwKICAgICAgIHNjaGVkX2RlcF90aW1lX21pbnMgPSB0aW1lMm1pbnMoc2NoZWRfZGVwX3RpbWUpKSAlPiUKICBzZWxlY3QoZGVwX3RpbWUsIGRlcF90aW1lX21pbnMsIHNjaGVkX2RlcF90aW1lLCBzY2hlZF9kZXBfdGltZV9taW5zKQpgYGAKCgoyLiBDb21wYXJlIGBhaXJfdGltZWAgd2l0aCBgYXJyX3RpbWUgLSBkZXBfdGltZWAuIFdoYXQgZG8geW91IGV4cGVjdCB0byBzZWU/IFdoYXQgZG8geW91IHNlZT8gV2hhdCBkbyB5b3UgbmVlZCB0byBkbyB0byBmaXggaXQ/CgpTaW5jZSBgYXJyX3RpbWVgIGFuZCBgZGVwX3RpbWVgIG1heSBiZSBpbiBkaWZmZXJlbnQgdGltZSB6b25lcywgdGhlIGBhaXJfdGltZWAgZG9lc24ndCBlcXVhbCB0aGUgZGlmZmVyZW5jZS4gCldlIHdvdWxkIG5lZWQgdG8gYWNjb3VudCBmb3IgdGltZS16b25lcyBpbiB0aGVzZSBjYWxjdWxhdGlvbnMuCmBgYHtyfQptdXRhdGUoZmxpZ2h0cywKICAgICAgIGFpcl90aW1lMiA9IGFycl90aW1lIC0gZGVwX3RpbWUsCiAgICAgICBhaXJfdGltZV9kaWZmID0gYWlyX3RpbWUyIC0gYWlyX3RpbWUpICU+JQogIGZpbHRlcihhaXJfdGltZV9kaWZmICE9IDApICU+JQogIHNlbGVjdChhaXJfdGltZSwgYWlyX3RpbWUyLCBkZXBfdGltZSwgYXJyX3RpbWUsIGRlc3QpCmBgYAoKCjMuIENvbXBhcmUgYGRlcF90aW1lYCwgYHNjaGVkX2RlcF90aW1lYCwgYW5kIGBkZXBfZGVsYXlgLiBIb3cgd291bGQgeW91IGV4cGVjdCB0aG9zZSB0aHJlZSBudW1iZXJzIHRvIGJlIHJlbGF0ZWQ/CgpJJ2QgZXhwZWN0IGBkZXBfdGltZWAsIGBzY2hlZF9kZXBfdGltZWAsIGFuZCBgZGVwX2RlbGF5YCB0byBiZSByZWxhdGVkIHNvIHRoYXQgYGRlcF90aW1lIC0gc2NoZWRfZGVwX3RpbWUgPSBkZXBfZGVsYXlgLgpgYGB7cn0KbXV0YXRlKGZsaWdodHMsCiAgICAgICBkZXBfZGVsYXkyID0gZGVwX3RpbWUgLSBzY2hlZF9kZXBfdGltZSkgJT4lCiAgZmlsdGVyKGRlcF9kZWxheTIgIT0gZGVwX2RlbGF5KSAlPiUKICBzZWxlY3QoZGVwX3RpbWUsIHNjaGVkX2RlcF90aW1lLCBkZXBfZGVsYXksIGRlcF9kZWxheTIpCmBgYApPb3BzLCBJIGZvcmdvdCB0byBjb252ZXJ0IHRvIG1pbnV0ZXMuIEknbGwgcmV1c2UgdGhlIGB0aW1lMm1pbnNgIGZ1bmN0aW9uIEkgd3JvdGUgZWFybGllci4KYGBge3J9Cm11dGF0ZShmbGlnaHRzLAogICAgICAgZGVwX2RlbGF5MiA9IHRpbWUybWlucyhkZXBfdGltZSkgLSB0aW1lMm1pbnMoc2NoZWRfZGVwX3RpbWUpKSAlPiUKICBmaWx0ZXIoZGVwX2RlbGF5MiAhPSBkZXBfZGVsYXkpICU+JQogIHNlbGVjdChkZXBfdGltZSwgc2NoZWRfZGVwX3RpbWUsIGRlcF9kZWxheSwgZGVwX2RlbGF5MikKYGBgCldlbGwsIHRoYXQgc29sdmVkIG1vc3Qgb2YgdGhlIHByb2JsZW1zLCBidXQgdGhlc2UgdHdvIG51bWJlcnMgZG9uJ3QgbWF0Y2ggYmVjYXVzZSB3ZSBhcmVuJ3QgYWNjb3VudGluZyBmb3IgZmxpZ2h0cyB3aGVyZSB0aGUgZGVwYXJ0dXJlIHRpbWUgaXMgdGhlIG5leHQgZGF5IGZyb20gdGhlIHNjaGVkdWxlZCBkZXBhcnR1cmUgdGltZS4gCgoKNC4gRmluZCB0aGUgMTAgbW9zdCBkZWxheWVkIGZsaWdodHMgdXNpbmcgYSByYW5raW5nIGZ1bmN0aW9uLiBIb3cgZG8geW91IHdhbnQgdG8gaGFuZGxlIHRpZXM/IENhcmVmdWxseSByZWFkIHRoZSBkb2N1bWVudGF0aW9uIGZvciBgbWluX3JhbmsoKWAuCgpJJ2Qgd2FudCB0byBoYW5kbGUgdGllcyBieSB0YWtpbmcgdGhlIG1pbmltdW0gb2YgdGllZCB2YWx1ZXMuIElmIHRocmVlIGZsaWdodHMgYXJlIGhhdmUgdGhlIHNhbWUgdmFsdWUgYW5kIGFyZSB0aGUgbW9zdCBkZWxheWVkLCB3ZSB3b3VsZCBzYXkgdGhleSBhcmUgdGllZCBmb3IgZmlyc3QsIG5vdCB0aWVkIGZvciB0aGlyZCBvciBzZWNvbmQuCmBgYHtyfQptdXRhdGUoZmxpZ2h0cywKICAgICAgIGRlcF9kZWxheV9yYW5rID0gbWluX3JhbmsoLWRlcF9kZWxheSkpICU+JQogIGFycmFuZ2UoZGVwX2RlbGF5X3JhbmspICU+JSAKICBmaWx0ZXIoZGVwX2RlbGF5X3JhbmsgPD0gMTApCmBgYAoKCjUuIFdoYXQgZG9lcyBgMTozICsgMToxMGAgcmV0dXJuPyBXaHk/CgpJdCByZXR1cm5zIGBjKDEgKyAxLCAyICsgMiwgMyArIDMsIDEgKyA0LCAyICsgNSwgMyArIDYsIDEgKyA3LCAyICsgOCwgMyArIDksIDEgKyAxMClgLgpXaGVuIGFkZGluZyB0d28gdmVjdG9ycyByZWN5Y2xlcyB0aGUgc2hvcnRlciB2ZWN0b3IncyB2YWx1ZXMgdG8gZ2V0IHZlY3RvcnMgb2YgdGhlIHNhbWUgbGVuZ3RoLgpXZSBnZXQgYSB3YXJuaW5nIHZlY3RvciBzaW5jZSB0aGUgc2hvcnRlciB2ZWN0b3IgaXMgbm90IGEgbXVsdGlwbGUgb2YgdGhlIGxvbmdlciBvbmUgKHRoaXMgb2Z0ZW4sIGJ1dCBub3QgbmVjZXNzYXJpbHksIG1lYW5zIHdlIG1hZGUgYW4gZXJyb3Igc29tZXdoZXJlKS4KCmBgYHtyfQoxOjMgKyAxOjEwCmBgYAoKCjYuIFdoYXQgdHJpZ29ub21ldHJpYyBmdW5jdGlvbnMgZG9lcyBSIHByb3ZpZGU/CgpBbGwgdGhlIGNsYXNzaWNzOiBgY29zYCwgYHNpbmAsIGB0YW5gLCBgYWNvc2AsIGBhc2luYCwgYGF0YW5gLCBwbHVzIGEgZmV3IG90aGVycyB0aGF0IGFyZSBkcml2ZSBieSBudW1lcmljYWwgb3IgY29tcHV0YXRpb25hbCBpc3N1ZXMuCgoKIyBFeGVyY2lzZXMKCjEuIEJyYWluc3Rvcm0gYXQgbGVhc3QgNSBkaWZmZXJlbnQgd2F5cyB0byBhc3Nlc3MgdGhlIHR5cGljYWwgZGVsYXkgY2hhcmFjdGVyaXN0aWNzIG9mIGEgZ3JvdXAgb2YgZmxpZ2h0cy4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBzY2VuYXJpb3M6CgogIC0gQSBmbGlnaHQgaXMgMTUgbWludXRlcyBlYXJseSA1MCUgb2YgdGhlIHRpbWUsIGFuZCAxNSBtaW51dGVzIGxhdGUgNTAlIG9mIHRoZSB0aW1lLgoKICAtIEEgZmxpZ2h0IGlzIGFsd2F5cyAxMCBtaW51dGVzIGxhdGUuCgogIC0gQSBmbGlnaHQgaXMgMzAgbWludXRlcyBlYXJseSA1MCUgb2YgdGhlIHRpbWUsIGFuZCAzMCBtaW51dGVzIGxhdGUgNTAlIG9mIHRoZSB0aW1lLgoKICAtIDk5JSBvZiB0aGUgdGltZSBhIGZsaWdodCBpcyBvbiB0aW1lLiAxJSBvZiB0aGUgdGltZSBpdOKAmXMgMiBob3VycyBsYXRlLgoKICBXaGljaCBpcyBtb3JlIGltcG9ydGFudDogYXJyaXZhbCBkZWxheSBvciBkZXBhcnR1cmUgZGVsYXk/CgpBcnJpdmFsIGRlbGF5IGlzIG1vcmUgaW1wb3J0YW50LgpBcnJpdmluZyBlYXJseSBpcyBuaWNlLCBidXQgZXF1YWxseSBhcyBnb29kIGFzIGFycml2aW5nIGxhdGUgaXMgYmFkLiAKVmFyaWF0aW9uIGlzIHdvcnNlIHRoYW4gY29uc2lzdGVuY3k7IGlmIEkga25vdyB0aGUgcGxhbmUgd2lsbCBhbHdheXMgYXJyaXZlIDEwIG1pbnV0ZXMgbGF0ZSwgdGhlbiBJIGNhbiBwbGFuIGZvciBpdCBhcnJpdmluZyBhcyBpZiB0aGUgYWN0dWFsIGFycml2YWwgdGltZSB3YXMgMTAgbWludXRlcyBsYXRlciB0aGFuIHRoZSBzY2hlZHVsZWQgYXJyaXZhbCB0aW1lLgoKU28gSSdkIHRyeSBzb21ldGhpbmcgdGhhdCBjYWxjdWxhdGVzIHRoZSBleHBlY3RlZCB0aW1lIG9mIHRoZSBmbGlnaHQsIGFuZCB0aGVuIGFnZ3JlZ2F0ZXMgb3ZlciBhbnkgZGVsYXlzIGZyb20gdGhhdCB0aW1lLiBJIHdvdWxkIGlnbm9yZSBhbnkgZWFybHkgYXJyaXZhbCB0aW1lcy4KQSBiZXR0ZXIgcmFua2luZyB3b3VsZCBhbHNvIGNvbnNpZGVyIGNhbmNlbGxhdGlvbnMsIGFuZCBuZWVkIGEgd2F5IHRvIGNvbnZlcnQgdGhlbSB0byBhIGRlbGF5IHRpbWUgKHBlcmhhcHMgdXNpbmcgdGhlIGFycml2YWwgdGltZSBvZiB0aGUgbmV4dCBmbGlnaHQgdG8gdGhlIHNhbWUgZGVzdGluYXRpb24pLgoKMi4gQ29tZSB1cCB3aXRoIGFub3RoZXIgYXBwcm9hY2ggdGhhdCB3aWxsIGdpdmUgeW91IHRoZSBzYW1lIG91dHB1dCBhcyBgbm90X2NhbmNlbGxlZCAlPiUgY291bnQoZGVzdClgIGFuZCBgbm90X2NhbmNlbGxlZCAlPiUgY291bnQodGFpbG51bSwgd3QgPSBkaXN0YW5jZSlgICh3aXRob3V0IHVzaW5nIGBjb3VudCgpYCkuCgoKCgozLiBPdXIgZGVmaW5pdGlvbiBvZiBjYW5jZWxsZWQgZmxpZ2h0cyBgKGlzLm5hKGRlcF9kZWxheSkgfCBpcy5uYShhcnJfZGVsYXkpKWAgaXMgc2xpZ2h0bHkgc3Vib3B0aW1hbC4gV2h5PyBXaGljaCBpcyB0aGUgbW9zdCBpbXBvcnRhbnQgY29sdW1uPwoKSWYgYSBmbGlnaHQgZG9lc24ndCBkZXBhcnQsIHRoZW4gaXQgd29uJ3QgYXJyaXZlLiBBIGZsaWdodCBjYW4gYWxzbyBkZXBhcnQgYW5kIG5vdCBhcnJpdmUgaWYgaXQgY3Jhc2hlczsgSSdtIG5vdCBzdXJlIGhvdyB0aGlzIGRhdGEgd291bGQgaGFuZGxlIGZsaWdodHMgdGhhdCBhcmUgcmVkaXJlY3RlZCBhbmQgbGFuZCBhdCBvdGhlciBhaXJwb3J0cyBmb3Igd2hhdGV2ZXIgcmVhc29uLgoKVGhlIG1vcmUgaW1wb3J0YW50IGNvbHVtbiBpcyBgYXJyX2RlbGF5YCBzbyB3ZSBjb3VsZCBqdXN0IHVzZSB0aGF0LgpgYGB7cn0KZmlsdGVyKGZsaWdodHMsICFpcy5uYShkZXBfZGVsYXkpLCBpcy5uYShhcnJfZGVsYXkpKSAlPiUKICBzZWxlY3QoZGVwX3RpbWUsIGFycl90aW1lLCBzY2hlZF9hcnJfdGltZSwgZGVwX2RlbGF5LCBhcnJfZGVsYXkpCmBgYApPa2F5LCBJJ20gbm90IHN1cmUgd2hhdCdzIGdvaW5nIG9uIGluIHRoaXMgZGF0YS4gYGRlcF90aW1lYCBjYW4gYmUgbm9uLW1pc3NpbmcgYW5kIGBhcnJfZGVsYXlgIG1pc3NpbmcgYnV0IGBhcnJfdGltZWAgbm90IG1pc3NpbmcuClRoZXkgbWF5IGJlIGNvbWJpbmluZyBkaWZmZXJlbnQgZmxpZ2h0cz8KCjQuIExvb2sgYXQgdGhlIG51bWJlciBvZiBjYW5jZWxsZWQgZmxpZ2h0cyBwZXIgZGF5LiBJcyB0aGVyZSBhIHBhdHRlcm4/IElzIHRoZSBwcm9wb3J0aW9uIG9mIGNhbmNlbGxlZCBmbGlnaHRzIHJlbGF0ZWQgdG8gdGhlIGF2ZXJhZ2UgZGVsYXk/CgpgYGB7cn0KY2FuY2VsbGVkX2RlbGF5ZWQgPC0gCiAgZmxpZ2h0cyAlPiUKICBtdXRhdGUoY2FuY2VsbGVkID0gKGlzLm5hKGFycl9kZWxheSkgfCBpcy5uYShkZXBfZGVsYXkpKSkgJT4lCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSkgJT4lCiAgc3VtbWFyaXNlKHByb3BfY2FuY2VsbGVkID0gbWVhbihjYW5jZWxsZWQpLAogICAgICAgICAgICBhdmdfZGVwX2RlbGF5ID0gbWVhbihkZXBfZGVsYXksIG5hLnJtID0gVFJVRSkpCgpnZ3Bsb3QoY2FuY2VsbGVkX2RlbGF5ZWQsIGFlcyh4ID0gYXZnX2RlcF9kZWxheSwgcHJvcF9jYW5jZWxsZWQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpCmBgYAoKCjUuIFdoaWNoIGNhcnJpZXIgaGFzIHRoZSB3b3JzdCBkZWxheXM/IENoYWxsZW5nZTogY2FuIHlvdSBkaXNlbnRhbmdsZSB0aGUgZWZmZWN0cyBvZiBiYWQgYWlycG9ydHMgdnMuIGJhZCBjYXJyaWVycz8gV2h5L3doeSBub3Q/IChIaW50OiB0aGluayBhYm91dCBgZmxpZ2h0cyAlPiUgZ3JvdXBfYnkoY2FycmllciwgZGVzdCkgJT4lIHN1bW1hcmlzZShuKCkpYCkKCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KGNhcnJpZXIpICU+JQogIHN1bW1hcmlzZShhcnJfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgYXJyYW5nZShkZXNjKGFycl9kZWxheSkpCmBgYAoKYGBge3J9CmZpbHRlcihhaXJsaW5lcywgY2FycmllciA9PSAiRjkiKQpgYGAKCkZyb250aWVyIEFpcmxpbmVzIChGTCkgaGFzIHRoZSB3b3JzdCBkZWxheXMuCgpZb3UgY2FuIGdldCBwYXJ0IG9mIHRoZSB3YXkgdG8gZGlzZW50YW5nbGluZyB0aGUgZWZmZWN0cyBvZiBhaXJwb3J0cyB2cy4gY2FycmllcnMgYnkgCmNvbXBhcmluZyBlYWNoIGZsaWdodCdzIGRlbGF5IHRvIHRoZSBhdmVyYWdlIGRlbGF5IG9mIGRlc3RpbmF0aW9uIGFpcnBvcnQuCkhvd2V2ZXIsIHlvdSdkIHJlYWxseSB3YW50IHRvIGNvbXBhcmUgaXQgdG8gdGhlIGF2ZXJhZ2UgZGVsYXkgb2YgdGhlIGRlc2luYXRpb24gYWlycG9ydCwgKmFmdGVyKiByZW1vdmluZyBvdGhlciBmbGlnaHRzIGZyb20gdGhlIHNhbWUgYWlybGluZS4KCjUzOCBoYXMgZG9uZSBzb21ldGhpbmcgbGlrZSB0aGlzOiBodHRwOi8vZml2ZXRoaXJ0eWVpZ2h0LmNvbS9mZWF0dXJlcy90aGUtYmVzdC1hbmQtd29yc3QtYWlybGluZXMtYWlycG9ydHMtYW5kLWZsaWdodHMtc3VtbWVyLTIwMTUtdXBkYXRlLy4KCgo2LiBGb3IgZWFjaCBwbGFuZSwgY291bnQgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJlZm9yZSB0aGUgZmlyc3QgZGVsYXkgb2YgZ3JlYXRlciB0aGFuIDEgaG91ci4KCkkgdGhpbmsgdGhpcyByZXF1aXJlcyBncm91cGVkIG11dGF0ZSAoYnV0IEkgbWF5IGJlIHdyb25nKToKYGBge3J9CmZsaWdodHMgJT4lCiAgYXJyYW5nZSh0YWlsbnVtLCB5ZWFyLCBtb250aCwgZGF5KSAlPiUKICBncm91cF9ieSh0YWlsbnVtKSAlPiUKICBtdXRhdGUoZGVsYXlfZ3QxaHIgPSBkZXBfZGVsYXkgPiA2MCkgJT4lCiAgbXV0YXRlKGJlZm9yZV9kZWxheSA9IGN1bXN1bShkZWxheV9ndDFocikpICU+JQogIGZpbHRlcihiZWZvcmVfZGVsYXkgPCAxKSAlPiUKICBjb3VudChzb3J0ID0gVFJVRSkKYGBgCgoKNy4gV2hhdCBkb2VzIHRoZSBzb3J0IGFyZ3VtZW50IHRvIGBjb3VudCgpYCBkby4gV2hlbiBtaWdodCB5b3UgdXNlIGl0PwoKVGhlIHNvcnQgYXJndW1lbnQgdG8gYGNvdW50YCBzb3J0cyB0aGUgcmVzdWx0cyBpbiBvcmRlciBvZiBgbmAuCllvdSBjb3VsZCB1c2UgdGhpcyBhbnl0aW1lIHlvdSB3b3VsZCBkbyBgY291bnRgIGZvbGxvd2VkIGJ5IGBhcnJhbmdlYC4KCiMgR3JvdXBlZCBtdXRhdGVzIGFuZCBmaWx0ZXJzCgojIyBFeGVyY2lzZXMKCjEuIFJlZmVyIGJhY2sgdG8gdGhlIHRhYmxlIG9mIHVzZWZ1bCBtdXRhdGUgYW5kIGZpbHRlcmluZyBmdW5jdGlvbnMuIERlc2NyaWJlIGhvdyBlYWNoIG9wZXJhdGlvbiBjaGFuZ2VzIHdoZW4geW91IGNvbWJpbmUgaXQgd2l0aCBncm91cGluZy4KClRoZXkgb3BlcmF0ZSB3aXRoaW4gZWFjaCBncm91cCByYXRoZXIgdGhhbiBvdmVyIHRoZSBlbnRpcmUgZGF0YSBmcmFtZS4gRS5nLiBgbWVhbmAgd2lsbCBjYWxjdWxhdGUgdGhlIG1lYW4gd2l0aGluIGVhY2ggZ3JvdXAuCgoyLiBXaGljaCBwbGFuZSAodGFpbG51bSkgaGFzIHRoZSB3b3JzdCBvbi10aW1lIHJlY29yZD8KCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KHRhaWxudW0pICU+JSAKICBzdW1tYXJpc2UoYXJyX2RlbGF5ID0gbWVhbihhcnJfZGVsYXksIG5hLnJtID0gVFJVRSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIocmFuayhkZXNjKGFycl9kZWxheSkpIDw9IDEpCgpgYGAKCgozLiBXaGF0IHRpbWUgb2YgZGF5IHNob3VsZCB5b3UgZmx5IGlmIHlvdSB3YW50IHRvIGF2b2lkIGRlbGF5cyBhcyBtdWNoIGFzIHBvc3NpYmxlPwoKTGV0J3MgZ3JvdXAgYnkgaG91ci4gVGhlIGVhcmxpZXIgdGhlIGJldHRlciB0byBmbHkuIFRoaXMgaXMgaW50dWl0aXZlIGFzIGRlbGF5cyBlYXJseSBpbiB0aGUgbW9ybmluZyBhcmUgbGlrZWx5IHRvIHByb3BvZ2F0ZSB0aHJvdWdob3V0IHRoZSBkYXkuCmBgYHtyfQpmbGlnaHRzICU+JQogIGdyb3VwX2J5KGhvdXIpICU+JQogIHN1bW1hcmlzZShhcnJfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoYXJyX2RlbGF5KQoKYGBgCgoKNC4gRm9yIGVhY2ggZGVzdGluYXRpb24sIGNvbXB1dGUgdGhlIHRvdGFsIG1pbnV0ZXMgb2YgZGVsYXkuIEZvciBlYWNoLCBmbGlnaHQsIGNvbXB1dGUgdGhlIHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIGRlbGF5IGZvciBpdHMgZGVzdGluYXRpb24uCgpgYGB7cn0KZmxpZ2h0cyAlPiUgCiAgZmlsdGVyKCFpcy5uYShhcnJfZGVsYXkpLCBhcnJfZGVsYXkgPiAwKSAlPiUgIAogIGdyb3VwX2J5KGRlc3QpICU+JQogIG11dGF0ZSh0b3RhbF9kZWxheSA9IHN1bShhcnJfZGVsYXkpLAogICAgICAgICBwcm9wX2RlbGF5ID0gYXJyX2RlbGF5IC8gc3VtKGFycl9kZWxheSkpCiAgCmBgYAoKQWx0ZXJuYXRpdmVseSwgY29uc2lkZXIgdGhlIGRlbGF5IGFzIHJlbGF0aXZlIHRvIHRoZSAqbWluaW11bSogZGVsYXkgZm9yIGFueSBmbGlnaHQgdG8gdGhhdCBkZXN0aW5hdGlvbi4gTm93IGFsbCBub24tY2FuY2VsbGVkIGZsaWdodHMgaGF2ZSBhIHByb3BvcnRpb24uCmBgYHtyfQpmbGlnaHRzICU+JSAKICBmaWx0ZXIoIWlzLm5hKGFycl9kZWxheSksIGFycl9kZWxheSA+IDApICU+JSAgCiAgZ3JvdXBfYnkoZGVzdCkgJT4lCiAgbXV0YXRlKHRvdGFsX2RlbGF5ID0gc3VtKGFycl9kZWxheSAtIG1pbihhcnJfZGVsYXkpKSwKICAgICAgICAgcHJvcF9kZWxheSA9IGFycl9kZWxheSAvIHN1bShhcnJfZGVsYXkpKQpgYGAKCgoKNS4gRGVsYXlzIGFyZSB0eXBpY2FsbHkgdGVtcG9yYWxseSBjb3JyZWxhdGVkOiBldmVuIG9uY2UgdGhlIHByb2JsZW0gdGhhdCBjYXVzZWQgdGhlIGluaXRpYWwgZGVsYXkgaGFzIGJlZW4gcmVzb2x2ZWQsIGxhdGVyIGZsaWdodHMgYXJlIGRlbGF5ZWQgdG8gYWxsb3cgZWFybGllciBmbGlnaHRzIHRvIGxlYXZlLiBVc2luZyBgbGFnKClgIGV4cGxvcmUgaG93IHRoZSBkZWxheSBvZiBhIGZsaWdodCBpcyByZWxhdGVkIHRvIHRoZSBkZWxheSBvZiB0aGUgaW1tZWRpYXRlbHkgcHJlY2VkaW5nIGZsaWdodC4KCldlIHdhbnQgdG8gZ3JvdXAgYnkgZGF5IHRvIGF2b2lkIHRha2luZyB0aGUgbGFnIGZyb20gdGhlIHByZXZpb3VzIGRheS4gCkFsc28sIEkgd2FudCB0byB1c2UgZGVwYXJ0dXJlIGRlbGF5LCBzaW5jZSB0aGlzIG1lY2hhbmlzbSBpcyByZWxldmFudCBmb3IgZGVwYXJ0dXJlcy4gCkFsc28sIEkgcmVtb3ZlIG1pc3NpbmcgdmFsdWVzIGJvdGggYmVmb3JlIGFuZCBhZnRlciBjYWxjdWxhdGluZyB0aGUgbGFnIGRlbGF5LgpIb3dldmVyLCBpdCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byBhc2sgdGhlIHByb2JhYmlsaXR5IG9yIGF2ZXJnZSBkZWxheSBhZnRlciBhIGNhbmNlbGxhdGlvbi4KYGBge3J9CmZsaWdodHMgJT4lCiAgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheSkgJT4lCiAgZmlsdGVyKCFpcy5uYShkZXBfZGVsYXkpKSAlPiUKICBtdXRhdGUobGFnX2RlbGF5ID0gbGFnKGRlcF9kZWxheSkpICU+JQogIGZpbHRlcighaXMubmEobGFnX2RlbGF5KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZGVwX2RlbGF5LCB5ID0gbGFnX2RlbGF5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQpgYGAKCgo2LiBMb29rIGF0IGVhY2ggZGVzdGluYXRpb24uIENhbiB5b3UgZmluZCBmbGlnaHRzIHRoYXQgYXJlIHN1c3BpY2lvdXNseSBmYXN0PyAoaS5lLiBmbGlnaHRzIHRoYXQgcmVwcmVzZW50IGEgcG90ZW50aWFsIGRhdGEgZW50cnkgZXJyb3IpLiBDb21wdXRlIHRoZSBhaXIgdGltZSBhIGZsaWdodCByZWxhdGl2ZSB0byB0aGUgc2hvcnRlc3QgZmxpZ2h0IHRvIHRoYXQgZGVzdGluYXRpb24uIFdoaWNoIGZsaWdodHMgd2VyZSBtb3N0IGRlbGF5ZWQgaW4gdGhlIGFpcj8KClRoZSBzaG9ydGVyIEJPUyBhbmQgUEhMIGZsaWdodHMgdGhhdCBhcmUgMjAgbWludXRlcyBmb3IgMzArIG1pbnV0ZXMgZmxpZ2h0cyBzZWVtIHBsYXVzaWJsZSAtIHRob3VnaCBtYXliZSBlbnRyaWVzIG9mICsvLSBhIGZldyBtaW51dGVzIGNhbiBlYXNpbHkgY3JlYXRlIGxhcmdlIGNoYW5nZXMuCkkgYXNzdW1lIHRoYXQgZGVwYXJ0dXJlIHRpbWUgaGFzIGEgc3RhbmRhcmRpemVkIGRlZmluaXRpb24sIGJ1dCBJJ20gbm90IHN1cmU7IGlmIHRoZXJlIGlzIHNvbWUgZGlzY3JldGlvbiwgdGhhdCBjb3VsZCBjcmVhdGUgZXJyb3JzIHRoYXQgYXJlIHNtYWxsIGluIGFic29sdXRlIHRpbWUsIGJ1dCBsYXJnZSBpbiByZWxhdGl2ZSB0aW1lIGZvciBzbWFsbCBmbGlnaHRzLgpUaGUgQVRMLCBHU1AsIGFuIEJOQSBmbGlnaHRzIGxvb2tzIGEgbGl0dGxlIHN1c3BpY2lvdXMgYXMgaXQncyBhbG1vc3QgaGFsZiB0aGUgdGltZSBmb3IgbG9uZ2VyIGZsaWdodHMuCmBgYHtyfQpmbGlnaHRzICU+JQogIGZpbHRlcighaXMubmEoYWlyX3RpbWUpKSAlPiUKICBncm91cF9ieShkZXN0KSAlPiUKICBtdXRhdGUobWVkX3RpbWUgPSBtZWRpYW4oYWlyX3RpbWUpLAogICAgICAgICBmYXN0ID0gKGFpcl90aW1lIC0gbWVkX3RpbWUpIC8gbWVkX3RpbWUpICU+JQogIGFycmFuZ2UoZmFzdCkgJT4lCiAgc2VsZWN0KGFpcl90aW1lLCBtZWRfdGltZSwgZmFzdCwgZGVwX3RpbWUsIHNjaGVkX2RlcF90aW1lLCBhcnJfdGltZSwgc2NoZWRfYXJyX3RpbWUpICU+JQogIGhlYWQoMTUpCmBgYAoKSSBjb3VsZCBhbHNvIHRyeSBhIHotc2NvcmUuIFRob3VnaCB0aGUgc2QgYW5kIG1lYW4gd2lsbCBiZSBhZmZlY3RlZCBieSBsYXJnZSBkZWxheXMuCmBgYHtyfQpmbGlnaHRzICU+JQogIGZpbHRlcighaXMubmEoYWlyX3RpbWUpKSAlPiUKICBncm91cF9ieShkZXN0KSAlPiUKICBtdXRhdGUoYWlyX3RpbWVfbWVhbiA9IG1lYW4oYWlyX3RpbWUpLAogICAgICAgICBhaXJfdGltZV9zZCA9IHNkKGFpcl90aW1lKSwKICAgICAgICAgel9zY29yZSA9IChhaXJfdGltZSAtIGFpcl90aW1lX21lYW4pIC8gYWlyX3RpbWVfc2QpICU+JQogIGFycmFuZ2Uoel9zY29yZSkgJT4lCiAgc2VsZWN0KHpfc2NvcmUsIGFpcl90aW1lX21lYW4sIGFpcl90aW1lX3NkLCBhaXJfdGltZSwgZGVwX3RpbWUsIHNjaGVkX2RlcF90aW1lLCBhcnJfdGltZSwgc2NoZWRfYXJyX3RpbWUpCmBgYAoKYGBge3J9CmZsaWdodHMgJT4lCiAgZmlsdGVyKCFpcy5uYShhaXJfdGltZSkpICU+JQogIGdyb3VwX2J5KGRlc3QpICU+JQogIG11dGF0ZShhaXJfdGltZV9kaWZmID0gYWlyX3RpbWUgLSBtaW4oYWlyX3RpbWUpKSAlPiUKICBhcnJhbmdlKGRlc2MoYWlyX3RpbWVfZGlmZikpICU+JQogIHNlbGVjdChkZXN0LCB5ZWFyLCBtb250aCwgZGF5LCBjYXJyaWVyLCBmbGlnaHQsIGFpcl90aW1lX2RpZmYsIGFpcl90aW1lLCBkZXBfdGltZSwgYXJyX3RpbWUpICU+JQogIGhlYWQoKQpgYGAKCgo3LiBGaW5kIGFsbCBkZXN0aW5hdGlvbnMgdGhhdCBhcmUgZmxvd24gYnkgYXQgbGVhc3QgdHdvIGNhcnJpZXJzLiBVc2UgdGhhdCBpbmZvcm1hdGlvbiB0byByYW5rIHRoZSBjYXJyaWVycy4KClRoZSBjYXJyaWVyIHRoYSBmbGllcyB0byB0aGUgbW9zdCBsb2NhdGlvbnMgaXMgRXhwcmVzc0pldCBBaXJsaW5lcyAoRVYpLgpFeHByZXNzSmV0IGlzIGEgcmVnaW9uYWwgYWlybGluZSBhbmQgcGFydG5lciBmb3IgbWFqb3IgYWlybGluZXMsIHNvIGl0cyBvbmUgb2YgdGhvc2UgdGhhdCBmbGllcyBzbWFsbCBwbGFuZXMgdG8gY2xvc2UgYWlycG9ydHMKCmBgYHtyfQpmbGlnaHRzICU+JSAKICBncm91cF9ieShkZXN0LCBjYXJyaWVyKSAlPiUKICBjb3VudChjYXJyaWVyKSAlPiUKICBncm91cF9ieShjYXJyaWVyKSAlPiUKICBjb3VudChzb3J0ID0gVFJVRSkKCmBgYAoKYGBge3J9CmZpbHRlcihhaXJsaW5lcywgY2FycmllciA9PSAiRVYiKQpgYGAKCg==